from TraceNode import TraceNode


class TraceNodeMaker:
    __minDuration: int
    __maxDuration: int
    __allTalks: list
    __traceNodes: list

    @property
    def allTalks(self):
        return self.__allTalks

    @property
    def matrix(self):
        return self.__matrix

    @property
    def allTalksCount(self) -> int:
        return len(self.allTalks)

    @property
    def maxDuration(self):
        return self.__maxDuration

    @property
    def minDuration(self):
        return self.__minDuration

    @property
    def traceNodes(self) -> list:
        if self.__traceNodes is None:
            if self.allTalksCount == 0:
                return []
            if self.totalSchemeCount() == 0:
                return []
            total_traces = []
            for total_time in range(self.minDuration, self.maxDuration + 1):
                if self.schemeCountForTime(total_time) > 0:
                    traces = []
                    self._lookBackToGetAllTrace(total_time, self.allTalksCount, None, traces)
                    total_traces = total_traces + traces
            self.__traceNodes = total_traces
        return self.__traceNodes

    def _generateMatrix(self) -> list:
        # 计算 (0 ~> 最大时间值) 各个时间的方案数目
        matrix = [[0 for _ in range(self.maxDuration + 1)] for _ in range(self.allTalksCount + 1)]
        matrix[0][0] = 1
        for i in range(1, self.allTalksCount + 1):
            for j in range(0, self.maxDuration + 1):
                duration = self.allTalks[i - 1].duration
                if duration <= j:
                    matrix[i][j] = matrix[i - 1][j] + matrix[i - 1][j - duration]
                else:
                    matrix[i][j] = matrix[i - 1][j]
        return matrix

    def __init__(self, max_duration, min_duration, talks: list):
        self.__maxDuration = max_duration
        self.__minDuration = min_duration
        self.__allTalks = talks
        self.__matrix = self._generateMatrix()
        self.__traceNodes = None

    def schemeCountForTime(self, total_time) -> int:
        return self.matrix[self.allTalksCount][total_time]

    def totalSchemeCount(self) -> int:
        time_range: [int] = range(self.minDuration, self.maxDuration + 1)
        time_range = map(lambda x: self.schemeCountForTime(x), time_range)
        from functools import reduce
        # 将范围内所有时间点的方案数相加
        return reduce(lambda x, y: x + y, time_range)

    def _lookBackToGetAllTrace(self, total_time, num_count, last, traces: list):
        if num_count == 0:
            return
        current_talk = self.allTalks[num_count - 1]
        uncluded = self.matrix[num_count - 1][total_time]
        included = self.matrix[num_count - 1][total_time - current_talk.duration]
        if uncluded > 0:
            self._lookBackToGetAllTrace(total_time, num_count - 1, last, traces)
        if included > 0:
            next_sum = total_time - current_talk.duration
            node = TraceNode(current_talk, last)
            if next_sum == 0:
                traces.append(node)
                return
            self._lookBackToGetAllTrace(next_sum, num_count - 1, node, traces)
